package com.zxx.libinject.apt;

import com.zxx.libinject.utils.AnnotationUtil;
import com.zxx.libinject.utils.IOUtil;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;

/**
 * @author zxx on 2021/5/10
 */
public abstract class AbsWriter implements AdapterWriter {

    public static final String SUFFIX = "$InjectAdapter";

    ProcessingEnvironment mProcessingEnv;
    Filer mFiler;

    public AbsWriter(ProcessingEnvironment mProcessingEnv) {
        this.mProcessingEnv = mProcessingEnv;
        mFiler = mProcessingEnv.getFiler();
    }

    @Override
    public void generate(Map<String, List<VariableElement>> typeMap) {
        Iterator<Map.Entry<String, List<VariableElement>>> iterator = typeMap.entrySet().iterator();

        while (iterator.hasNext()) {
            Map.Entry<String, List<VariableElement>> entry = iterator.next();
            List<VariableElement> cacheElements = entry.getValue();
            if (cacheElements == null || cacheElements.size() == 0) {
                continue;
            }

            //取第一个元素来构造注入信息
            InjectorInfo info = createInjectorInfo(cacheElements.get(0));
            Writer writer = null;
            JavaFileObject javaFileObject;

            try {
                javaFileObject = mFiler.createSourceFile(info.getClassFullPath());
                writer = javaFileObject.openWriter();

                //写入package，import，class以及findView函数等代码段
                generateImport(writer, info);

                // 写入该类中的所有字段到findViews方法中
                for (VariableElement element : entry.getValue()) {
                    writeField(writer, element, info);
                }

                // 写入findViews函数的大括号以及类的大括号
                writeEnd(writer);

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtil.closeQuitly(writer);
            }
        }

    }

    private InjectorInfo createInjectorInfo(VariableElement element) {
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        String packageName = AnnotationUtil.getPackageName(mProcessingEnv, typeElement);
        String className = typeElement.getSimpleName().toString();
        return new InjectorInfo(packageName, className);
    }


    protected abstract void generateImport(Writer writer, InjectorInfo info) throws IOException;

    protected abstract void writeField(Writer writer, VariableElement element, InjectorInfo info) throws IOException;

    protected abstract void writeEnd(Writer writer) throws IOException;


    /**
     * 注解相关的信息实体类
     */
    public static class InjectorInfo {
        public String packageName;      //被注解的类的包名
        public String className;        //被注解的类的类名
        /**
         * 要创建的InjectAdapter类的完整路径,新类的名字为被注解的类名 + "$InjectAdapter", 与被注解的类在同一个包下
         */
        public String newClassName;

        public InjectorInfo(String packageName, String className) {
            this.packageName = packageName;
            this.className = className;
            newClassName = className + SUFFIX;
        }

        public String getClassFullPath() {
            return packageName + File.separator + newClassName;
        }
    }
}
